'use strict';

var _ = require('lodash');
var constant = require('../utils/constant');

function MDeck() {
    this.tile = [];
    this.operPai = [];
    this.countMap = {};
}
exports.MDeck = MDeck;


MDeck.prototype.cleanTile = function () {
    this.tile = [];
    this.operPai = [];
    this.countMap = {};
};

MDeck.prototype.removeRepeatedTile = function () {
    var uniqList = [];
    var self = this;
    Object.keys(self.countMap).forEach(function (index) {
        if (!!self.countMap[index]) {
            uniqList.push(parseInt(index));
        }
    });
    return uniqList;
};

MDeck.prototype.sortTile = function () {
    this.tile.sort(function (a, b) {
        return a - b;
    });
};


MDeck.prototype.removeRepeatedTileAndDuiZiUnique = function () {
    var uniqList = [];
    var self = this;
    Object.keys(self.countMap).forEach(function (index) {
        if (self.countMap[index] === 4) {
            uniqList.push(parseInt(index));
            uniqList.push(parseInt(index));
        } else if (!!self.countMap[index]) {
            uniqList.push(parseInt(index));
        }
    });
    return uniqList;
};

MDeck.prototype.pushTile = function (t) {
    if (typeof t == 'number') {
        this.tile.push(t);
        if (this.countMap[t] === undefined) {
            this.countMap[t] = 1;
            return;
        }
        this.countMap[t]++;
    }
};

MDeck.prototype.length = function () {
    return this.tile.length;
};

MDeck.prototype.countTile = function (t) {
    return this.countMap[t] === undefined ? 0 : this.countMap[t];
};

MDeck.prototype.removeTile = function (t) {
    var index = this.tile.indexOf(t);
    if (index !== -1) {
        this.tile.splice(index, 1);
    }
    if (this.countMap[t] !== undefined) {
        this.countMap[t]--;
    }
    if (this.countMap[t] === 0) {
        delete this.countMap[t];
    }
};

MDeck.prototype.extractDuizi = function () {
    this.sortTile();
    var dr = this.removeRepeatedTileAndDuiZiUnique();
    //console.log(dr);
    var e = new MDeckExtract();
    var self = this;
    dr.forEach(function (pai) {
        var dt = _.cloneDeep(self);
        var de = new MDeck();
        if (dt.countTile(pai) > 1) {
            dt.removeTile(pai);
            dt.removeTile(pai);

            de.pushTile(pai);
            de.pushTile(pai);

            e.extracted[e.methodCount] = de;
            e.remained[e.methodCount] = dt;
            e.methodCount++;
        }
    });
    return e;
};

MDeck.prototype.extractShunzi = function () {
    this.sortTile();
    var dr = this.removeRepeatedTile();
    var e = new MDeckExtract();
    var self = this;
    dr.forEach(function (pai) {
        var dt = _.cloneDeep(self);
        var de = new MDeck();
        if ((pai + 1) % 9 > pai % 9 &&
            (pai + 2) % 9 > (pai + 1) % 9 &&
            dt.countTile(pai + 1) > 0 &&
            dt.countTile(pai + 2) > 0) {
            dt.removeTile(pai);
            dt.removeTile(pai + 1);
            dt.removeTile(pai + 2);

            de.pushTile(pai);
            de.pushTile(pai + 1);
            de.pushTile(pai + 2);

            e.extracted[e.methodCount] = de;
            e.remained[e.methodCount] = dt;
            e.methodCount++;
        }
    });
    return e;
};

MDeck.prototype.extractKezi = function () {
    this.sortTile();
    var dr = this.removeRepeatedTile();
    var e = new MDeckExtract();
    var self = this;
    dr.forEach(function (pai) {
        var dt = _.cloneDeep(self);
        var de = new MDeck();
        if (dt.countTile(pai) > 2) {
            dt.removeTile(pai);
            dt.removeTile(pai);
            dt.removeTile(pai);

            de.pushTile(pai);
            de.pushTile(pai);
            de.pushTile(pai);

            e.extracted[e.methodCount] = de;
            e.remained[e.methodCount] = dt;
            e.methodCount++;
        }
    });
    return e;
};

// 判定是否是对子
MDeck.prototype.isDuizi = function () {
    if (this.tile.length !== 2) {
        return false;
    }
    return this.countMap[this.tile[0]] === 2;
};

// 上半张  7,8  听 6 
MDeck.prototype.isLeftzhang = function () {
    if (this.tile.length !== 2) {
        return false;
    }
    var firstPai = this.tile[0];
    if (this.countMap[firstPai] &&
        this.countMap[firstPai + 1] &&
        (firstPai + 1) % 9 === 8) {
        return true;
    }
    return false;
};
//  下半张 0,1 听 2
MDeck.prototype.isRightzhang = function () {
    if (this.tile.length !== 2) {
        return false;
    }
    var firstPai = this.tile[0];
    if (this.countMap[firstPai] &&
        this.countMap[firstPai + 1] &&
        (firstPai) % 9 === 0) {
        return true;
    }
    return false;
};

//  中间那张 0,2 听 1
MDeck.prototype.isCenterzhang = function () {
    if (this.tile.length !== 2) {
        return false;
    }
    var firstPai = this.tile[0];
    if (this.countMap[firstPai] &&
        this.countMap[firstPai + 2] &&
        (firstPai) % 9 >= 0 &&
        (firstPai + 1) % 9 !== 0 &&
        (firstPai + 2) % 9 > firstPai % 9) {
        return true;
    }
    return false;
};
//  两边两张 2,3 听 1, 4
MDeck.prototype.isBothzhang = function () {
    if (this.tile.length !== 2) {
        return false;
    }
    var firstPai = this.tile[0];
    if (this.countMap[firstPai] &&
        this.countMap[firstPai + 1] &&
        (firstPai) % 9 > 0 &&
        (firstPai + 1) % 9 !== 0) {
        return true;
    }
    return false;
};

// 1. 检查牌堆长度是否被3除余2（因为和牌情况只能是2、5、8、11、14等张数的情形），若不是，返回false。
// 2. 检查牌堆是否满足特殊情况，例如七对子，若是，返回true，否则继续。
// 3. 检查牌堆长度是否为2，若是，则牌堆两张相同则返回true，否则返回false；若长度不为2，则继续。
// 4. 对牌堆进行顺子提取，然后对提取结果中的remain(Deck数组)里的每个元素(Deck)分别执行该算法，只要有一个元素(Deck)执行结果为true，则直接返回true，否则继续。
// 5. 对牌堆进行刻子提取，然后对提取结果中的remain(Deck数组)里的每个元素(Deck)分别执行该算法，只要有一个元素(Deck)执行结果为true，则直接返回true，否则继续。
// 6. 返回false。
MDeck.prototype.isHu = function () {
    var duiZi = this.extractDuizi();
    var keZi = this.extractKezi();
    if (this.length() === 14) {
        // 七小对 / 龙七对 胡牌不做区分
        if (duiZi.methodCount === 7) {
            return true;
        }
        // // 大对子
        if (keZi.methodCount === 4) {
            if (duiZi.methodCount === 1) {
                return true;
            }
        }
    } else {
        var tempDeck = _.cloneDeep(this); //copy this 数据
        tempDeck.combineDeck(tempDeck.operPai); // 连接手牌，和操作过的牌;
        var tkeZi = tempDeck.extractKezi();
        var tduiZi = tempDeck.extractDuizi();
        if (tkeZi.methodCount === 4) {
            if (tduiZi.methodCount === 1) {
                return true;
            }
        }
    }

    if (this.length() % 3 !== 2) {
        return false;
    }

    if (this.length() === 2) {
        if (this.isDuizi()) {
            return true;
        }
        return false;
    }

    for (var i = 0; i < keZi.methodCount; i++) {
        if (keZi.remained[i].isHu()) {
            return true;
        }
    }

    var shunZi = this.extractShunzi();
    for (i = 0; i < shunZi.methodCount; i++) {
        if (shunZi.remained[i].isHu()) {
            return true;
        }
    }
    return false;
};

MDeck.prototype.combineDeck = function (mdeckValue) {
    for (var i in mdeckValue) {
        this.pushTile(mdeckValue[i]);
    }
    return this;
};

MDeck.prototype.pushOperPai = function (pais) {
    if (_.isArray(pais)) {
        this.operPai = pais;
    }
    return this;
};

MDeck.prototype.operLength = function () {
    return this.operPai.length;
};

//获取麻将类型
function getMJType(id) {
    if (id >= 0 && id < 9) {
        //筒
        return constant.PaiType.TONG;
    }
    else if (id >= 9 && id < 18) {
        //条
        return constant.PaiType.TIAO;
    }
    else if (id >= 18 && id < 27) {
        //万
        return constant.PaiType.WAN;
    }
}

function checkAllPaiTypeIsSame(list) {
    var type = getMJType(list[0]);
    for (var i = 1; i < list.length; i++) {
        if (getMJType(list[i]) !== type) {
            return false;
        }
    }
    return true;
}

function checkIsKuangZhangLianPai(hodlMap, type, beginTingPai, endTingPai) {
    var yiSePais = [];
    Object.keys(hodlMap).forEach(function (pai) {
        if (getMJType(pai) == type) {
            yiSePais.push(pai);
        }
    });
    var removeBeginValue = [];
    for (var i = 0; i < yiSePais.length; i++) {
        if (yiSePais[i] < beginTingPai) {
            continue;
        }
        if (yiSePais[i] > endTingPai) {
            break;
        }
        removeBeginValue.push(yiSePais[i]);
    }
    var value = removeBeginValue[0];
    for (i = 0; i < removeBeginValue.length; i++) {
        if (removeBeginValue[i] != value) {
            return false;
        }
        value++;
    }
    return true;
}




// 听牌跟和牌不一样，因为听牌方式有3种，分别是摸一张构成对子（单骑）、刻子（对倒）、顺子（边张或者砍张）。而构成牌的数目也不同，分别是2、3、3，因此，
// 听牌牌堆的长度应该满足被3除余1，因此被3整除的情形是肯定不能算听牌的。
// 该算法返回牌堆能听的所有牌，大致步骤如下：
// 3. 检查牌堆长度是否为13，若是，检查是否有6组对子，如果有，则听剩余的那张牌（七对子的规则），将其加入返回结果；不管有没有6组对子，都继续进行。
// 4. 检查牌堆长度是否为1，若是，将仅有的这张牌加入返回结果（单骑听牌），然后继续进行。
// 5. 检查牌堆长度是否为2，若是，分以下情况进行讨论：
// a) 两张牌相同，将这张牌加入返回结果（双碰听牌），然后继续进行。
// b) 两张牌相邻，将两张牌往两边扩展的那张牌（例如7,8则扩展的牌为6和9，若为8和9则只有7）均加入返回结果（边张听牌），然后继续进行。
// c) 两张牌隔一，将这两张牌中间的那张牌（例如6,8则中间那张牌为7）加入返回结果（砍张听牌），然后继续进行。
// d) 不满足以上三种情形，则不加入，并继续进行。
// 6. 检查牌堆长度是否为4，若是，依次对其进行对子提取，刻子提取和顺子提取，并将提取结果的remain数组里的每个元素(Deck)都做听牌算法，并将结果悉数加入返回结果，然后继续进行。
// 7. 检查牌堆长度是否大于4，若是，依次对其进行刻子提取和顺子提取（同6一样，只不过不做对子提取），并将提取结果的remain数组里的每个元素(Deck)都做听牌算法，并将结果悉数加入返回结果，然后继续进行。
// 8. 将这个返回结果的非重复牌堆副本作为返回值，OK。


//  听牌类型列表 



MDeck.prototype.tingPai = function () {
    var dr;
    var result = [];
    var tingPais = [];
    var isBianKaDiao = true;
    //  先听大对子
    if (this.length() + this.operLength() === 13) {
        var tempDeck = _.cloneDeep(this); //copy this 数据
        tempDeck.combineDeck(tempDeck.operPai); // 连接手牌，和操作过的牌;

        var tduiZi = tempDeck.extractDuizi();
        var tkeZi = tempDeck.extractKezi();
        dr = tempDeck.removeRepeatedTile();
        // 听大对子
        if (tkeZi.methodCount === 4) {
            tingPais = [];
            for (i = 0; i < dr.length; i++) {
                if (tempDeck.countTile(dr[i]) === 1) {
                    tingPais.push(dr[i]);
                    result.push({
                        tingPais: tingPais,
                        type: constant.TingPaiType.DADUIZI
                    });
                }
            }
        }
        if (tkeZi.methodCount === 3) {
            if (tduiZi.methodCount === 5) {
                tingPais = [];
                var errDaDuiZi = false;
                for (i = 0; i < dr.length; i++) {
                    if (tempDeck.countTile(dr[i]) === 2) {
                        tingPais.push(dr[i]);
                    }
                    if (tempDeck.countTile(dr[i]) === 1) {
                        errDaDuiZi = true;
                    }
                }
                if (!errDaDuiZi) {
                    result.push({
                        tingPais: tingPais,
                        type: constant.TingPaiType.DADUIZI
                    });
                }
            }
        }
        if (result.length > 0) {   //大对子不会是边卡吊
            isBianKaDiao = false;
        }
    }
    if (this.length() === 13) {
        dr = this.removeRepeatedTile();
        var duiZi = this.extractDuizi();
        var keZi = this.extractKezi();
        if (duiZi.methodCount === 6) {   // 优先龙七对
            if (keZi.methodCount >= 1) { // 龙七对
                //先判断龙七对
                for (var i = 0; i < dr.length; i++) {
                    if (this.countTile(dr[i]) === 3) {
                        tingPais.push(dr[i]);
                        result.push({
                            tingPais: tingPais,
                            type: constant.TingPaiType.LONGQIDUI
                        });
                    }
                }
                //如果不能胡龙七对，则继续判断七小对
                if (result.length === 0) {
                    for (i = 0; i < dr.length; i++) {
                        if (this.countTile(dr[i]) === 1) {
                            tingPais.push(dr[i]);
                            result.push({
                                tingPais: tingPais,
                                type: constant.TingPaiType.QIXIAODUI
                            });
                        }
                    }
                }
            } else {
                tingPais = [];
                for (i = 0; i < dr.length; i++) { //  七小对
                    if (this.countTile(dr[i]) === 1) {
                        tingPais.push(dr[i]);
                        result.push({
                            tingPais: tingPais,
                            type: constant.TingPaiType.QIXIAODUI
                        });
                    }
                }
            }
            if (result.length > 0) {  //龙七对/七小对也不可能是边卡吊
                isBianKaDiao = false;
            }
        }
    }
    //单吊边卡吊 
    var type;
    if (this.length() === 1 && isBianKaDiao) {
        tingPais = [];
        tingPais.push(this.tile[0]);
        type = constant.TingPaiType.BIANKADIAO; // 普通胡牌
        result.push({
            tingPais: tingPais,
            type: type
        });
    }

    if (this.length() > 1) {
        // 1.  通过找将获得听牌集合
        tingPais = [];
        duiZi = this.extractDuizi();
        for (i = 0; i < duiZi.methodCount; i++) {
            tingPais = tingPais.concat(duiZi.remained[i].ting());
        }
        // 2. 直接听当前手牌获得听牌集合， 累加两步所得的集合
        tingPais = tingPais.concat(this.ting());
        if (tingPais.length > 0) { // 如果普通听牌有检测到听牌
            // 去掉重复听牌,并排序
            tingPais = _.uniq(tingPais);
            tingPais.sort(function (a, b) { return a - b; });
            type = constant.TingPaiType.PINGHU; // 普通胡牌
            // 判定是否为大宽张
            if (tingPais.length === 3) {
                if (checkAllPaiTypeIsSame(tingPais)) {
                    var pais = [tingPais[0] % 9, tingPais[1] % 9, tingPais[2] % 9];
                    if (pais.toString() === [0, 3, 6].toString() ||
                        pais.toString() === [1, 4, 7].toString() ||
                        pais.toString() === [2, 5, 8].toString()) {
                        if (checkIsKuangZhangLianPai(this.countMap, getMJType(tingPais[0]), tingPais[0], tingPais[2])) {
                            type = constant.TingPaiType.DAKUANGZI;
                        }
                    }
                }
            }

            // 听特殊牌型，有关联可能被普通牌型算法检测。
            if (result.length === 1) { // 已有听特殊牌型， 普通牌型检测是否和特殊牌型听的牌一致，若一致则为边卡吊。
                if (tingPais.length === 1 && result[0].tingPais[0] === tingPais[0]) {
                    type = constant.TingPaiType.BIANKADIAO;// 边卡吊
                }
            }
            if (result.length === 0) { // 无听特殊牌型， 则只需要检测普通听牌是否为一张就为边卡吊。
                if (tingPais.length === 1) {
                    type = constant.TingPaiType.BIANKADIAO;// 边卡吊
                }
            }
            if (result.length === 0 ||  //无特殊胡牌
                (result.length === 1 && type !== constant.TingPaiType.BIANKADIAO && type !== constant.TingPaiType.DAKUANGZI) || //有特殊胡牌，且胡牌类型不能是边卡吊 , [大宽不能叠加特殊胡牌]
                (result.length === 1 && isBianKaDiao)) {  //有特殊胡牌，能胡边卡吊
                result.push({
                    tingPais: tingPais,
                    type: type
                });
            }
        } else { //普通听牌没检测到听牌
            if (result[0] && result[0].tingPais.length === 1) { // 如果特殊听牌有听到牌，且普通听牌没检测到, 则特殊听牌可以赋加单吊
                if (isBianKaDiao) { //能叠加边卡吊的才允许叠加
                    type = constant.TingPaiType.BIANKADIAO; // 边卡吊
                    result.push({
                        tingPais: [result[0].tingPais[0]],
                        type: type
                    });
                }
            }
        }
    }
    return result;
};



MDeck.prototype.ting = function () {
    var result = new MDeck();
    //单吊
    if (this.length() === 1) {
        result.pushTile(this.tile[0]);
    }
    if (this.length() === 2) {
        if (this.isDuizi()) {
            result.pushTile(this.tile[0]);
        } else if (this.isLeftzhang()) {
            result.pushTile(this.tile[0] - 1);
        } else if (this.isRightzhang()) {
            result.pushTile(this.tile[0] + 2);
        } else if (this.isCenterzhang()) {
            result.pushTile(this.tile[0] + 1);
        } else if (this.isBothzhang()) {
            result.pushTile(this.tile[0] - 1);
            result.pushTile(this.tile[0] + 2);
        }
    }

    if (this.length() >= 4) {
        var keZi = this.extractKezi();
        for (var i = 0; i < keZi.methodCount; i++) {
            result.combineDeck(keZi.remained[i].ting());
        }
        var shunZi = this.extractShunzi();

        for (i = 0; i < shunZi.methodCount; i++) {
            result.combineDeck(shunZi.remained[i].ting());
        }
    }
    return result.removeRepeatedTile();
};


function MDeckExtract() {
    this.methodCount = 0;
    this.extracted = {};
    this.remained = {};
}

module.exports = {
    MDeck: MDeck
};


//筒 0 ~ 8 表示筒子,条 9 ~ 17表示条子,万 18 ~ 26表示万
var mdeck = new MDeck();

//  9 10 11 12 13 14 15 16 17 17 11
// mdeck.pushTile(7);
// mdeck.pushTile(5);
// mdeck.pushTile(5);
// mdeck.pushTile(23);
// mdeck.pushTile(23);
// mdeck.pushTile(17);
// mdeck.pushTile(18);
// mdeck.pushTile(18);
// mdeck.pushTile(22);
// mdeck.pushTile(22);
// mdeck.pushTile(26);
// mdeck.pushTile(26);
// mdeck.pushTile(26);
// mdeck.pushTile(26);
// mdeck.pushTile(7);

//mdeck.pushOperPai([8, 8, 8, 19, 19, 19, 22, 22, 22]);

// mdeck.pushTile(4);

// mdeck.pushTile(5);
// mdeck.pushTile(1);
// mdeck.pushTile(11);
// mdeck.pushTile(12);
// mdeck.pushTile(13);
// mdeck.pushTile(14);
// mdeck.pushTile(3);
// mdeck.pushTile(3);
// mdeck.pushTile(1);
// mdeck.pushTile(1);
// mdeck.pushTile(1);
// mdeck.pushTile(2);
// mdeck.pushTile(2);
// mdeck.pushTile(2);






// mdeck.pushTile(2);

// mdeck.pushTile(4);
// mdeck.pushTile(4);
// mdeck.pushTile(4);
// mdeck.pushTile(5);
// mdeck.pushTile(5);
// mdeck.pushTile(5);
// mdeck.pushTile(6);
// mdeck.pushTile(6);
// mdeck.pushTile(6);

// mdeck.pushTile(11);
// mdeck.pushTile(12);
// mdeck.pushTile(13);

// mdeck.pushTile(5);
// mdeck.pushTile(5);
// mdeck.pushTile(7);
// mdeck.pushTile(8);
// mdeck.pushTile(5);

// mdeck.pushTile(0);

// mdeck.pushTile(0);
// mdeck.pushTile(0);

// mdeck.pushTile(3);

// mdeck.pushTile(2);
// mdeck.pushTile(2);
// mdeck.pushTile(2);

// mdeck.pushTile(3);
// mdeck.pushTile(3);
// mdeck.pushTile(3);

// mdeck.pushTile(4);
// mdeck.pushTile(4);
// mdeck.pushTile(4);

// mdeck.pushTile(6);
// mdeck.pushTile(6);

//var holds = [11,13,15,9,12,17,16,13,17,16,15,10,9];
//var holds = [2,3,4,4,5,18,18,20,20,20];
//var holds = [11,21,11,20,14,22,21,11,10,24,13,17,11];
//var holds = [1,3,4,4,6,7,8,18,19,20,20,21,22,2];
var holds = [ 15, 3, 6, 9, 7, 20, 19, 24, 1, 9, 11, 11, 12, 3 ];
holds.sort(function(a,b){return a-b;});
console.log(holds);
var pengPai = [];
mdeck.combineDeck(holds).pushOperPai(pengPai);
// mdeck.pushTile(5);
//console.log(mdeck.isHu());

//var time = process.hrtime();
// for (var i = 0; i < 10000; i++) {
// console.log(mdeck.length() );
//if (mdeck.length() % 3 === 1) {
    //var tingResult = [];
    //tingResult = mdeck.tingPai();
    //console.log(tingResult);
    // if(!~mdeck.tingPai()[0].tingPais.indexOf(24)) {
    //     console.log("jia");
    // }
//}
// }

// var diff = process.hrtime(time);
// console.log(diff);

// var tingResult = [];
// if (mdeck.length() % 3 === 1) {  // 听牌必须是能被3整除余1
//     //console.log("user id == " + seatData.userId + " check ting pai begin holds == " + seatData.holds);
//     tingResult = mdeck.tingPai();
// }
// if (tingResult != null && tingResult.length > 0) {
//     //seatData.tingResult = tingResult;
//     console.log("ting res setting == ", tingResult);
// }